package org.qianji.goosql.dql;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.qianji.goosql.Fragment;
import org.qianji.goosql.cond.Condition;
import org.qianji.goosql.cond.Conditions;
import org.qianji.goosql.dql.clause.From;
import org.qianji.goosql.dql.clause.GroupBy;
import org.qianji.goosql.dql.clause.OrderBy;
import org.qianji.goosql.dql.clause.Select;
import org.qianji.goosql.dql.clause.SimpleFrom;
import org.qianji.goosql.dql.clause.SimpleSelect;
import org.qianji.goosql.dql.clause.SimpleWhere;
import org.qianji.goosql.dql.clause.Where;
import org.qianji.goosql.exception.DQLException;
import org.qianji.goosql.field.Field;
import org.qianji.goosql.table.Table;

/**
 * 查询语句。
 * 
 * @author gmz
 * 
 */
public class QueryStatement implements Fragment {

	/**
	 * 
	 */
	private static final long serialVersionUID = -1418158224591431327L;

	private Select select;
	private From from;
	private Where where;
	private GroupBy groupBy;
	private OrderBy orderBy;

	/**
	 * 默认构造子。
	 */
	public QueryStatement() {
		super();
	}

	/**
	 * 获取查询子句。
	 * 
	 * @return {@link Select} 查询子句
	 */
	public Select getSelect() {
		return select;
	}

	/**
	 * 获取From子句。
	 * 
	 * @return {@link From} From子句
	 */
	public From getFrom() {
		return from;
	}

	/**
	 * 获取Where子句。
	 * 
	 * @return {@link Where} Where子句
	 */
	public Where getWhere() {
		return where;
	}

	/**
	 * 获取分组子句。
	 * 
	 * @return {@link GroupBy} 分组子句
	 */
	public GroupBy getGroupBy() {
		return groupBy;
	}

	/**
	 * 获取排序子句。
	 * 
	 * @return {@link OrderBy} 排序子句
	 */
	public OrderBy getOrderBy() {
		return orderBy;
	}

	/**
	 * 设置查询子句。
	 * 
	 * @param select
	 *            {@link Select} 查询子句
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement select(Select select) {
		this.select = select;
		return this;
	}

	/**
	 * 增加查询字段。
	 * 
	 * @param fields
	 *            {@link Field fields} 查询字段数组
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement select(Field... fields) {
		if (select == null) {
			select = new SimpleSelect();
		}
		if (fields != null && fields.length > 0) {
			for (Field fld : fields) {
				if (fld != null) {
					select.addQueryField(fld);
				}
			}
		}
		return this;
	}

	/**
	 * 设置新的查询字段，删除原有查询字段。
	 * 
	 * @param fields
	 *            {@link Field fields} 查询字段数组，如果未指定或为空则查询所有字段
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement selectNew(Field... fields) {
		select = (fields != null ? new SimpleSelect(Arrays.asList(fields)) : new SimpleSelect());
		return this;
	}

	/**
	 * 设置是否剔除重复结果。
	 * 
	 * @param distinct
	 *            {@link java.lang.Boolean boolean} true-剔除重复结果，false-不剔除
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement distinct(boolean distinct) {
		select.setDistinct(distinct);
		return this;
	}

	/**
	 * 设置From子句。
	 * 
	 * @param from
	 *            {@link From} From子句
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement from(From from) {
		this.from = from;
		return this;
	}

	/**
	 * 设置查询主表，替换原有查询表。
	 * 
	 * @param table
	 *            {@link Table} 表
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement from(Table table) {
		from = new SimpleFrom(table);
		return this;
	}

	/**
	 * 内关联，参见{@link From#innerJoin(Table, Condition)}。
	 * 
	 * @param table
	 *            {@link Table} 表
	 * @param cond
	 *            {@link Condition} 关联条件
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement innerJoin(Table table, Condition cond) {
		from = from.innerJoin(table, cond);
		return this;
	}

	/**
	 * 左关联，参见{@link From#leftJoin(Table, Condition)}。
	 * 
	 * @param table
	 *            {@link Table} 表
	 * @param cond
	 *            {@link Condition} 关联条件
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement leftJoin(Table table, Condition cond) {
		from = from.leftJoin(table, cond);
		return this;
	}

	/**
	 * 右关联，参见{@link From#rightJoin(Table, Condition)}。
	 * 
	 * @param table
	 *            {@link Table} 表
	 * @param cond
	 *            {@link Condition} 关联条件
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement rightJoin(Table table, Condition cond) {
		from = from.rightJoin(table, cond);
		return this;
	}

	/**
	 * 全关联，参见{@link From#fullJoin(Table, Condition)}。
	 * 
	 * @param table
	 *            {@link Table} 表
	 * @param cond
	 *            {@link Condition} 关联条件
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement fullJoin(Table table, Condition cond) {
		from = from.fullJoin(table, cond);
		return this;
	}

	/**
	 * 设置Where子句。
	 * 
	 * @param where
	 *            {@link Where} Where子句
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement where(Where where) {
		this.where = where;
		return this;
	}

	/**
	 * 添加查询条件，与原有条件以AND连接。
	 * 
	 * @param conds
	 *            {@link Condition} 查询条件数组
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement where(Condition... conds) {
		if (where == null) {
			where = new SimpleWhere(conds);
		} else {
			if (conds != null && conds.length > 0) {
				for (Condition cond : conds) {
					if (cond != null) {
						where.and(cond);
					}
				}
			}
		}
		return this;
	}

	/**
	 * 设置新的查询条件，删除原有条件。
	 * 
	 * @param conds
	 *            {@link Condition} 查询条件数组
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement whereNew(Condition... conds) {
		where = new SimpleWhere(conds);
		return this;
	}

	/**
	 * 设置分组子句。
	 * 
	 * @param groupBy
	 *            {@link GroupBy} 分组子句
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement groupBy(GroupBy groupBy) {
		this.groupBy = groupBy;
		return this;
	}

	/**
	 * 添加分组字段。
	 * 
	 * @param fields
	 *            {@link Field} 分组字段数组
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement groupBy(Field... fields) {
		if (groupBy == null) {
			groupBy = new GroupBy();
		}
		if (fields != null && fields.length > 0) {
			for (Field fld : fields) {
				if (fld != null) {
					groupBy.addGroupField(fld);
				}
			}
		}
		return this;
	}

	/**
	 * 设置新的分组字段。
	 * 
	 * @param fields
	 *            {@link Field} 分组字段数组
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement groupByNew(Field... fields) {
		groupBy = (fields != null ? new GroupBy(Arrays.asList(fields)) : new GroupBy());
		return this;
	}

	/**
	 * 添加分组条件，与原有条件以AND连接。
	 * 
	 * @param conds
	 *            {@link Condition} 分组条件数组
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement having(Condition... conds) {
		if (groupBy == null) {
			groupBy = new GroupBy();
		}
		if (conds != null && conds.length > 0) {
			for (Condition cond : conds) {
				if (cond != null) {
					groupBy.addHaving(cond);
				}
			}
		}
		return this;
	}

	/**
	 * 添加分组条件，与原有条件以AND连接。
	 * 
	 * @param conds
	 *            {@link Condition} 分组条件数组
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement havingNew(Condition... conds) {
		if (groupBy == null) {
			groupBy = new GroupBy();
		}
		groupBy.setHaving((conds != null && conds.length > 0) ? Conditions.and(conds) : null);
		return this;
	}

	/**
	 * 设置排序子句。
	 * 
	 * @param orderBy
	 *            {@link OrderBy} 排序子句
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement orderBy(OrderBy orderBy) {
		this.orderBy = orderBy;
		return this;
	}

	/**
	 * 添加排序字段。
	 * 
	 * @param field
	 *            {@link Field} 排序字段
	 * @param isAsc
	 *            {@link java.lang.Boolean boolean} 是否升序，true-升序，false-降序
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement orderBy(Field field, boolean isAsc) {
		if (orderBy == null) {
			orderBy = new OrderBy();
		}
		orderBy.addOrder(field, isAsc);
		return this;
	}

	/**
	 * 设置排序字段，删除原有排序字段。
	 * 
	 * @param field
	 *            {@link Field} 排序字段
	 * @param isAsc
	 *            {@link java.lang.Boolean boolean} 是否升序，true-升序，false-降序
	 * @return {@link QueryStatement} 查询语句
	 */
	public QueryStatement orderByNew(Field field, boolean isAsc) {
		orderBy = new OrderBy();
		orderBy.addOrder(field, isAsc);
		return this;
	}

	@Override
	public String toSql() {
		if (select == null || from == null) {
			throw new DQLException("select and from must't be null");
		}
		StringBuffer dql = new StringBuffer(select.toSql()).append(" ").append(from.toSql());
		if (where != null) {
			dql.append(" ").append(where.toSql());
		}
		if (groupBy != null) {
			dql.append(" ").append(groupBy.toSql());
		}
		if (orderBy != null) {
			dql.append(" ").append(orderBy.toSql());
		}
		return dql.toString();
	}

	@Override
	public List<Object> getPlacedParameters() {
		List<Object> ppl = new ArrayList<Object>();
		if (select != null) {
			ppl.addAll(select.getPlacedParameters());
		}
		if (from != null) {
			ppl.addAll(from.getPlacedParameters());
		}
		if (where != null) {
			ppl.addAll(where.getPlacedParameters());
		}
		if (groupBy != null) {
			ppl.addAll(groupBy.getPlacedParameters());
		}
		if (orderBy != null) {
			ppl.addAll(orderBy.getPlacedParameters());
		}
		return ppl;
	}

	@Override
	public Map<String, Object> getNamedParameters() {
		Map<String, Object> npm = new HashMap<String, Object>();
		if (select != null) {
			npm.putAll(select.getNamedParameters());
		}
		if (from != null) {
			npm.putAll(from.getNamedParameters());
		}
		if (where != null) {
			npm.putAll(where.getNamedParameters());
		}
		if (groupBy != null) {
			npm.putAll(groupBy.getNamedParameters());
		}
		if (orderBy != null) {
			npm.putAll(orderBy.getNamedParameters());
		}
		return npm;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null || !(obj instanceof QueryStatement)) {
			return false;
		}
		QueryStatement other = (QueryStatement) obj;
		if (((select == null && other.getSelect() == null) || select.equals(other.getSelect()))
				&& ((from == null && other.getFrom() == null) || from.equals(other.getFrom()))
				&& ((where == null && other.getWhere() == null) || where.equals(other.getWhere()))
				&& ((groupBy == null && other.getGroupBy() == null) || groupBy.equals(other.getGroupBy()))
				&& ((orderBy == null && other.getOrderBy() == null) || orderBy.equals(other.getOrderBy()))) {
			return true;
		}
		return false;
	}

	@Override
	public int hashCode() {
		int hc = 17;
		if (select != null) {
			hc = 37 * hc + select.hashCode();
		}
		if (from != null) {
			hc = 37 * hc + from.hashCode();
		}
		if (where != null) {
			hc = 37 * hc + where.hashCode();
		}
		if (groupBy != null) {
			hc = 37 * hc + groupBy.hashCode();
		}
		if (orderBy != null) {
			hc = 37 * hc + orderBy.hashCode();
		}
		return hc;
	}

	@Override
	public String toString() {
		return toSql();
	}

}
